package com.xyzwps.server.paimon.app

import com.xyzwps.server.paimon.app.dto.RegisterInput
import com.xyzwps.server.paimon.app.dto.RegisterSendVcodeInput
import com.xyzwps.server.paimon.error.Errors
import com.xyzwps.server.paimon.common.Utils
import com.xyzwps.server.paimon.ext.instantNow
import com.xyzwps.server.paimon.infra.service.SmsService
import com.xyzwps.server.paimon.model.entity.User
import com.xyzwps.server.paimon.value.VcodeScene
import com.xyzwps.server.paimon.model.repository.UserRepository
import com.xyzwps.server.paimon.model.service.PhoneVcodeService
import org.jetbrains.exposed.v1.jdbc.transactions.transaction
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional

@Service
class RegisterAppService(
    private val userRepository: UserRepository,
    private val phoneVcodeService: PhoneVcodeService,
    private val smsService: SmsService
) {

    // TODO: 应用层检查参数
    @Transactional
    fun register(input: RegisterInput) = transaction {
        val phone = input.phone
        val _user = userRepository.findByPhone(phone)
        if (_user != null) {
            throw Errors.PHONE_HAS_BEEN_REGISTERED.toEx()
        }

        phoneVcodeService.validate(
            phone = phone,
            vcode = input.vcode,
            dkey = input.dkey,
            scene = VcodeScene.REGISTER
        )

        val user = User(
            id = null,
            nickname = Utils.maskPhone(phone),
            phone = phone,
            createdAt = instantNow(),
        )
        userRepository.save(user)
    }

    @Transactional
    fun sendRegisterVcode(input: RegisterSendVcodeInput): String = transaction {
        val phone = input.phone
        val user = userRepository.findByPhone(phone)
        if (user != null) {
            // If the user already exists, directly return a dkey to pretend the user exists,
            // preventing this interface from being used to test whether the phone number is
            // registered.
            return@transaction phoneVcodeService.dkey()
        }

        val vcode = phoneVcodeService.generate(phone, VcodeScene.REGISTER)
        smsService.asyncSendRegisterVcode(phone, vcode.vcode)
        return@transaction vcode.dkey
    }
}
